package Scuttlebutt;

import java.net.URL;
import java.net.MalformedURLException;
import java.io.*;
import java.text.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.Toolkit;
import java.awt.FontMetrics;
import java.util.regex.*;
import java.util.*;
import java.util.TooManyListenersException;
import javax.swing.undo.*;
import javax.swing.*;
//import javax.swing.text.html.HTMLEditorKit;
//import javax.swing.text.html.HTMLDocument;
//import javax.swing.text.html.HTMLDocument.HTMLReader;
//import javax.swing.text.html.parser.ParserDelegator;
import javax.swing.text.*;
import javax.swing.event.*;
import java.awt.Image;

public class Tab extends JTextArea implements DropTargetListener, KeyListener//, FocusListener 
{
	String file = null;
    public static final int UNDO_LIMIT = 1500;
    private UndoManager m_undoManager = null;
	UndoAction undoAction = null;
	RedoAction redoAction = null;
	Scuttlebutt parent = null;
	boolean isChanged = false;
	boolean bNeedsSaving = false;

	public class THandler extends TransferHandler{
	    private DataFlavor stringFlavor = DataFlavor.stringFlavor;
	    private JTextComponent source;
	    private boolean shouldRemove;
	    
    	Position p0 = null, p1 = null;
    	
		public THandler(){
		}
		public boolean importData(JComponent comp, Transferable t){
	        JTextComponent tc = (JTextComponent)comp;
	
	        if (!canImport(comp, t.getTransferDataFlavors())) {
	            return false;
	        }
	
	        if (tc.equals(source) && (tc.getCaretPosition() >= p0.getOffset()) &&
	                                 (tc.getCaretPosition() <= p1.getOffset())) {
	            shouldRemove = false;
	            return true;
	        }
	
	        if (hasStringFlavor(t.getTransferDataFlavors())) {
	            try {
	                String str = (String)t.getTransferData(stringFlavor);
	                tc.replaceSelection(str);
	                return true;
	            } catch (UnsupportedFlavorException ufe) {
	                System.out.println("importData: unsupported data flavor");
	            } catch (IOException ioe) {
	                System.out.println("importData: I/O exception");
	            }
	        }
	        //The ColorTransferHandler superclass handles color.
	        return super.importData(comp, t);
		}
	    //Remove the old text if the action is a MOVE.
	    //However, we do not allow dropping on top of the selected text,
	    //so in that case do nothing.
	    protected void exportDone(JComponent c, Transferable data, int action) {
	        if (shouldRemove && (action == MOVE)) {
	            if ((p0 != null) && (p1 != null) &&
	                (p0.getOffset() != p1.getOffset())) {
	                try {
	                    JTextComponent tc = (JTextComponent)c;
	                    tc.getDocument().remove(
	                       p0.getOffset(), p1.getOffset() - p0.getOffset());
	                } catch (BadLocationException e) {
	                    System.out.println("Can't remove text from source.");
	                }
	            }
	        }
	        source = null;
	    }
	    //Create a Transferable implementation that contains the
	    //selected text.
	    protected Transferable createTransferable(JComponent c) {
	        source = (JTextComponent)c;
	        int start = source.getSelectionStart();
	        int end = source.getSelectionEnd();
	        Document doc = source.getDocument();
	        if (start == end) {
	            return null;
	        }
	        try {
	            p0 = doc.createPosition(start);
	            p1 = doc.createPosition(end);
	        } catch (BadLocationException e) {
	            System.out.println(
	              "Can't create position - unable to remove text from source.");
	        }
	        shouldRemove = true;
	        String data = source.getSelectedText();
	        return new StringSelection(data);
	    }
	
	    public int getSourceActions(JComponent c) {
	        return COPY_OR_MOVE;
	    }

	
	    /**
	     * Does flavors contain a color or string Transferable?
	     */
	    public boolean canImport(JComponent c, DataFlavor[] flavors) {
	        if (hasStringFlavor(flavors)) {
	            return true;
	        }
	        return super.canImport(c, flavors);
	    }
	
	    /**
	     * Does the flavor list have a string flavor?
	     */
	    protected boolean hasStringFlavor(DataFlavor[] flavors) {
	        for (int i = 0; i < flavors.length; i++) {
	            if (stringFlavor.equals(flavors[i])) {
	                return true;
	            }
	        }
	        return false;
	    }
	}
	
    class UndoAction extends AbstractAction {
        public UndoAction() {
            super("Undo");
            setEnabled(false);
        }

        public void actionPerformed(ActionEvent e) {
            try {
                m_undoManager.undo();
            } catch (CannotUndoException ex) {
                System.out.println("Unable to undo: " + ex);
                ex.printStackTrace();
            }
            updateUndoState();
            redoAction.updateRedoState();
        }

        protected void updateUndoState() {
            if (m_undoManager.canUndo()) {
                setEnabled(true);
                putValue(Action.NAME, m_undoManager.getUndoPresentationName());
            } else {
                setEnabled(false);
                putValue(Action.NAME, "Undo");
            }
        }
    }

    class RedoAction extends AbstractAction {
        public RedoAction() {
            super("Redo");
            setEnabled(false);
        }

        public void actionPerformed(ActionEvent e) {
            try {
                m_undoManager.redo();
            } catch (CannotRedoException ex) {
                System.out.println("Unable to redo: " + ex);
                ex.printStackTrace();
            }
            updateRedoState();
            undoAction.updateUndoState();
        }

        protected void updateRedoState() {
            if (m_undoManager.canRedo()) {
                setEnabled(true);
                putValue(Action.NAME, m_undoManager.getRedoPresentationName());
            } else {
                setEnabled(false);
                putValue(Action.NAME, "Redo");
            }
        }
    }
	
    //This one listens for edits that can be undone.
    protected class MyUndoableEditListener
                    implements UndoableEditListener {
        public void undoableEditHappened(UndoableEditEvent e) {
            //Remember the edit and update the menus.
            m_undoManager.addEdit(e.getEdit());
            undoAction.updateUndoState();
            redoAction.updateRedoState();
        }
    }
    
    public class TabPopupMenu extends JPopupMenu
	{
		Tab tab = null;
		public TabPopupMenu(Tab tab){
			this.tab = tab;
			
			JMenuItem menuItem = null;	
			add(new UndoMenuItem(parent.getEditPane()));
			add(new RedoMenuItem(parent.getEditPane()));
			
			
			addSeparator();
			menuItem = new JMenuItem(new DefaultEditorKit.CutAction());
			menuItem.setText("Cut");
			add(menuItem);
			menuItem = new JMenuItem(new DefaultEditorKit.CopyAction());
			menuItem.setText("Copy");
			add(menuItem);
			menuItem = new JMenuItem(new DefaultEditorKit.PasteAction());
			menuItem.setText("Paste");
			add(menuItem);
			add(new EventDeleteEdit("Delete",tab));
			addSeparator();
			add(new EventSelectAllEdit("Select All", tab));
			add(new EventTimeEdit("Time", tab));
			add(new EventDateEdit("Date", tab));	
			
			((JMenuItem)getComponent(0)).setEnabled(tab.canUndo());
			((JMenuItem)getComponent(1)).setEnabled(tab.canRedo());
			((JMenuItem)getComponent(3)).setEnabled(tab.canCut());
			((JMenuItem)getComponent(4)).setEnabled(tab.canCopy());
			((JMenuItem)getComponent(5)).setEnabled(tab.canPaste());
			((JMenuItem)getComponent(6)).setEnabled(tab.canDelete());
			((JMenuItem)getComponent(8)).setEnabled((tab.getText().length() > 0)?true:false);
			((JMenuItem)getComponent(9)).setEnabled(tab.canTime());
			((JMenuItem)getComponent(10)).setEnabled(tab.canDate());
			
			
		}
	}

	public class TabMouseEventHandler implements MouseListener 
	{
		Tab tab = null;
		
		public TabMouseEventHandler(Tab tab)
		{
			this.tab = tab;
		}
	
		public void mouseClicked(MouseEvent m0)
		{
			if(m0.getButton() == m0.BUTTON3){
				TabPopupMenu pop = new TabPopupMenu(tab);
				pop.show(tab, m0.getX(), m0.getY());
			}
		}
		
		public void mouseEntered(MouseEvent m0)
		{
		}
	
		public void mouseExited(MouseEvent m0)
		{
		}
	
		public void mousePressed(MouseEvent m0)
		{
		}
	
		public void mouseReleased(MouseEvent m0)
		{
		}
	}
	
	public Tab()
	{
		super(50,50);
		setTabSize(2);
		setEditable(true);
		THandler tHandler = new THandler();
		setTransferHandler(tHandler);
//		HTMLEditorKit editorKit = new HTMLEditorKit();
//		setEditorKitForContentType("text/html", editorKit);
		setCaretPosition(0);
//		setTabSize(4);
		m_undoManager = new UndoManager();
		undoAction = new UndoAction();
		redoAction = new RedoAction();
		getDocument().addUndoableEditListener(new MyUndoableEditListener());
		requestFocusInWindow();
		getCaret().setVisible(true);
		setDropTarget(new DropTarget(this, this));	
		addKeyListener(this);
		addMouseListener(new TabMouseEventHandler(this));
	}
	
/*	public Tab(Scuttlebutt parent, String text){
//		this.browse = browse;
		this.parent = parent;
		file = new String("");
		setText(text);
		setEditable(true);
		THandler tHandler = new THandler();
		setTransferHandler(tHandler);
		setCaretPosition(0);
		m_undoManager = new UndoManager();
		undoAction = new UndoAction();
		redoAction = new RedoAction();
		getDocument().addUndoableEditListener(new MyUndoableEditListener());
		requestFocusInWindow();
		getCaret().setVisible(true);
		setDropTarget(new DropTarget(this, this));	
		addKeyListener(this);

	}
*/	
	public Tab(Scuttlebutt parent, String file){
		super(50,50);
		setTabSize(2);
		
		this.parent = parent;
		
//		System.out.println("new tab:" + file);
//		HTMLEditorKit editorKit = new HTMLEditorKit();
//		setEditorKitForContentType("text/html", editorKit);
//		Open(file);
		this.file = file;
		Open(file);
//		addFocusListener(this);
		setEditable(true);
//		setTabSize(4);
		THandler tHandler = new THandler();
		setTransferHandler(tHandler);
		setCaretPosition(0);
		m_undoManager = new UndoManager();
		undoAction = new UndoAction();
		redoAction = new RedoAction();
		getDocument().addUndoableEditListener(new MyUndoableEditListener());
		requestFocusInWindow();
		getCaret().setVisible(true);
		setDropTarget(new DropTarget(this, this));	
		addKeyListener(this);
		addMouseListener(new TabMouseEventHandler(this));
	}

	
	private void Open(String f){
		StringBuilder buffer = null;
		try{
		    buffer = new StringBuilder();
			BufferedReader r = new BufferedReader(new FileReader(f));
	 		int val = 0;
	 		while( (val = r.read()) != -1){
		 		buffer.append((char)val);
	 		}
	 		r.close();
		}catch(IOException io){
			System.err.print(io.getMessage());
		}

		setText(buffer.toString());
		getCaret().setVisible(true);
	}
	
	
/*	public void Open(String file){
//		this.file = file;
		this.file = file;
		
		try{
			javax.swing.text.html.HTMLDocument doc = new javax.swing.text.html.HTMLDocument();
			read(new FileInputStream(file), doc); 
		}catch(FileNotFoundException fnf){
			System.err.print(fnf.getMessage());
		}catch(IOException io){
			System.err.print(io.getMessage());
		}
	}
*/	
/*	public void setTabSize(int charPerTab){
		FontMetrics fm = getFontMetrics( getFont() );
		int charWidth = fm.charWidth( 'w' );
		int tabWidth = charWidth * charPerTab;
 
		TabStop[] tabs = new TabStop[10];
 
		for (int j = 0; j < tabs.length; j++)
		{
			int tab = j + 1;
			tabs[j] = new TabStop( tab * tabWidth );
		}
 
		TabSet tabSet = new TabSet(tabs);
		SimpleAttributeSet attributes = new SimpleAttributeSet();
		StyleConstants.setTabSet(attributes, tabSet);
		int length = getDocument().getLength();
		getStyledDocument().setParagraphAttributes(0, length, attributes, true);	
	}
*/	

	public boolean needsToBeSaved(){
		return bNeedsSaving;
	}

	public boolean canUndo(){
		return m_undoManager.canUndo();
	}
	
	public boolean canRedo(){
		return m_undoManager.canRedo();
	}
	
	public boolean canCut(){
		if ( getSelectionEnd() - getSelectionStart() > 0)
			return true;
		return false;
	}
	public boolean canCopy(){
		return canCut();
	}
	
	public boolean canDelete(){
		return canCut();
	}
	
	public boolean canTime(){
		if(getCaretPosition() >= 0)
			return true;
		else
			return false;
	}

	public boolean canDate(){
		return canTime();
	}
	public boolean canSelectAll(){
		return canCut();
	}
	
	public boolean canPaste(){
		if(!hasFocus())
			return false;
		boolean can = false;
		Clipboard clipboard = Toolkit.getDefaultToolkit ().getSystemClipboard();
		try{
			if(clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)){
				String text = clipboard.getData(DataFlavor.stringFlavor).toString();
		 		if(text.length() > 0)
		 			can = true;
				}
		}catch(IOException e){
			System.err.print(e + "\n");
		}catch(UnsupportedFlavorException e){
			System.err.print(e + "\n");
		}
		return can;
	}

	public void undo(){
	   	try
	    {
	        //Undo changes
	        m_undoManager.undo();
	    }
	    catch(CannotUndoException cue)
	    {
	        System.out.print(cue.getMessage() + "\n");
	    }
	}
	
	public void redo(){
        try
        {
            //Redo changes
            m_undoManager.redo();
        }
        catch(CannotRedoException cue)
        {
            System.err.print(cue.getMessage() + "\n");
        }
	}
	
	public void Delete(){
		try{
			getDocument().remove(getSelectionStart(), getSelectionEnd() - getSelectionStart());
		}catch(BadLocationException e){
			System.err.println(e.getMessage());
		}
	}
	
	public void InsertTime(){
		SimpleDateFormat df = new SimpleDateFormat(new String("h:mm a"));
		try{
			getDocument().insertString(getCaretPosition(), df.format(new Date()), null);
		    if(isEditable()){
				Changed();
				bNeedsSaving = true;
		    }
		}catch(BadLocationException e){
			System.err.println(e.getMessage());
		}
	}

	public void InsertDate(){
		SimpleDateFormat df = new SimpleDateFormat(new String("MMMM d, yyyy"));
		try{
			getDocument().insertString(getCaretPosition(), df.format(new Date()), null);
		    if(isEditable()){
				Changed();
				bNeedsSaving = true;
		    }
		}catch(BadLocationException e){
			System.err.println(e.getMessage());
		}
	}
	
	public void setFile(String file){
//		this.file = file;
		this.file = file;
	}
	
	public String getFile(){
		return file;
	}
	
	public void dragEnter(DropTargetDragEvent dtde){
	}
	
	public void dragOver(DropTargetDragEvent dtde){
	}
	
	public void dropActionChanged(DropTargetDragEvent dtde){
	}
	
	public void dragExit(DropTargetEvent dte){
	}
	
	public void drop(DropTargetDropEvent dtde){
		dtde.acceptDrop(dtde.getDropAction());
		Transferable t = dtde.getTransferable();
		DataFlavor [] f = t.getTransferDataFlavors();
		DataFlavor dfurl = null;
		DataFlavor dffl = null;
		DataFlavor df_node = null;
		try{
			dfurl = new DataFlavor("application/x-java-url; class=java.net.URL");
			dffl = new DataFlavor("application/x-java-file-list; class=java.util.List");
			df_node = new DataFlavor("application/node");
//			df_node = new DataFlavor("application/x-java-jvm-local-objectref; class=java.lang.String");
			
		}catch(java.lang.ClassNotFoundException cnf){
		}
		
/*		for(DataFlavor d : f){
			try{
				System.out.println("DataFlavor");
				System.out.println(d.getMimeType());
				System.out.println(t.getTransferData(d).toString());
				System.out.println("");
			}catch(UnsupportedFlavorException e){
				System.err.println(e.getMessage());
			}catch(IOException e){
				System.err.println(e.getMessage());
			}
		}
*/		
		for(DataFlavor d : f){
			try{
				if(dfurl.isMimeTypeEqual(d)){
					System.out.println("dfurl.isMimeTypeEqual(d)");
					String link = new String("<a href=\"" + t.getTransferData(d).toString() + "\">ADD TEXT HERE</a>");
					Document doc = getDocument();
					doc.insertString(getCaretPosition(),link,null);
					Changed();
					bNeedsSaving = true;
				}else if(df_node.isMimeTypeEqual(d)){
					
					Object o = t.getTransferData(d);
			        if (o instanceof FolderNode ) {
				        System.out.println("FolderNode");
			        } else if (o instanceof URLNode ){
				        System.out.println("URLNode");
				        if(isEditable()){
					        URLNode urlnode = (URLNode) o;
							String link = new String("<a href=\"" + urlnode.getURL() + "\">" +  urlnode.toString()+ "</a>");
							Document doc = getDocument();
							doc.insertString(getCaretPosition(),link,null);
							
							bNeedsSaving = true;
							Changed();
							parent.updateTree();
				        }
			        }else if (o instanceof SortableNode ){
//				        System.out.println("SortableNode");
					    if(isEditable()){
							String link = new String("<a href=\"http://" + parent.getProject().getAddress() + new String((((SortableNode)o).getRemoteDirectory().length() > 0)?new String(/*"/"*/String.valueOf(File.separatorChar) + ((SortableNode)o).getRemoteDirectory()) : "") + "/" + o.toString()+ "\">" +  o.toString()+ "</a>");
							Document doc = getDocument();
							doc.insertString(getCaretPosition(),link,null);
							((SortableNode)o).addFile(file);
							Changed();
							bNeedsSaving = true;
							parent.updateTree();
					    }
			        }
				}else if(DataFlavor.imageFlavor.isMimeTypeEqual(d)){
					String link = new String("<img src=\"" + t.getTransferData(d).toString() + "\"></img>");
					Document doc = getDocument();
					doc.insertString(getCaretPosition(),link,null);
					Changed();
					bNeedsSaving = true;
				}else if(d.isFlavorJavaFileListType()){
					List fileList = (List)t.getTransferData(d);
					for (int i = 0; i < fileList.size(); i++) {
						File data = (File)fileList.get(i);
						if(!data.isDirectory()){
							Document doc = getDocument();
							if((getExtension(data.getPath()).equalsIgnoreCase("jpg")) ||
							(getExtension(data.getPath()).equalsIgnoreCase("gif"))){
								String link = new String("<img src=\"" + getFilePath(data.getPath()) + "\" alt=\"Nice Try\"></img>");
								doc.insertString(getCaretPosition(),link,null);
								bNeedsSaving = true;
							}else if((getExtension(data.getPath()).equalsIgnoreCase("htm")) ||
							(getExtension(data.getPath()).equalsIgnoreCase("html"))){
								String link = new String("<a href=\"file:///" + data.getPath() + "\">ADD TEXT HERE</a>");
								doc.insertString(getCaretPosition(),link,null);
								bNeedsSaving = true;
							}else{
								doc.insertString(getCaretPosition(),data.getPath(),null);
								bNeedsSaving = true;
							}
							Save();
							
/*							try{
								if(file != null){
									Save();
									parent.getBrowsePane().LoadPage(new URL(new String("file:///" + file.getPath())));
								}else{
									parent.getBrowsePane().edit.setText("");
								}
							}catch(MalformedURLException me){
								
								System.err.println("EditPane MalformedUrlException");
								System.err.println(me.toString());
							}*/
						}
					}
					Changed();
				}else{
				}
				
			}catch(UnsupportedFlavorException e){
				System.err.println(e.getMessage());
			}catch(IOException e){
				System.err.println(e.getMessage());
			}catch(BadLocationException ble){
				System.err.println(ble.getMessage());
			}
		}
	}	
	
	public void replace(String dest, String source){
		System.out.println("replace");
		System.out.println("dest:" + dest);
		System.out.println("source:" + source);
/*		
		StringBuffer buffer = new StringBuffer(getText());
		int pos = buffer.indexOf(dest);
		while((pos != -1) && (pos < buffer.length())){
			buffer.replace(pos, pos + dest.length(),source);
//			insert(source,pos);
			pos = buffer.indexOf(dest, pos + dest.length());
		}
		
		System.out.println("buffer:" + buffer.toString());
		setText(buffer.toString());*/
		int pos = getText().indexOf(dest);
		System.out.println("pos:" + pos);
		while((pos != -1) && (pos < getText().length())){
			replaceRange("", pos, pos + dest.length());
			insert(source,pos);
			pos = getText().indexOf(dest, pos + dest.length());
			System.out.println("pos:" + pos);
		}
	}
	
	private String getFilePath(String f){
		if(file != null){
			Pattern p = Pattern.compile(new String("\\\\"));
			String source = new String(f);
			Matcher m = p.matcher(source);
			StringTokenizer tok = new StringTokenizer(file/*.getPath()*/,"\\");
			int parent_c = tok.countTokens() - 1;
			int searchPos = 0;
			int c = 0;
			while(m.find(searchPos)){
				int pos = m.start();
				if(c == parent_c - 1){
					return f.substring(pos + 1, f.length());
				}
				searchPos = pos + 1;
				++c;
			}
		}
		return f;//.getPath();
	}
	
    public String getExtension(String f) {
	    File nf = new File(f);
        String ext = null;
        String s = nf.getName();
        int i = s.lastIndexOf('.');

        if (i > 0 &&  i < s.length() - 1) {
            ext = s.substring(i+1).toLowerCase();
        }
        return ext;
    }
    
    public void undoNotUploaded(){
	    if(isChanged){
			SortableNode node = (SortableNode)parent.getProject().SearchTree((Object)parent.getRootNode(), file);
			node.setUploadingState(false);
			node.setUploaded(true);
	    }
    }
    
    public void Save(){
		try{
			System.out.println("tab.Save()");
			System.out.println("file:" + file);
			System.out.println("Rootnode:" + (Object)parent.getRootNode().toString());
			SortableNode node = (SortableNode)parent.getProject().SearchTree((Object)parent.getRootNode(), file);
			if(node != null){
				System.out.println("node:" + node.toString());
			}else{
				System.out.println("node == null");
			}
			if(!node.getUploadingState()){
				FileWriter b = new FileWriter(file);
				BufferedWriter w = new BufferedWriter(b);
				w.write(getText(), 0, getText().length());
				w.close();
				b.close();
				
				w = null;
				b = null;
				bNeedsSaving = false;
			}
		}catch(IOException io){
			System.out.print(io.getMessage());
		}
    }

    public void keyTyped(KeyEvent e){
	    if(isEditable()){
			Changed();
			bNeedsSaving = true;
	    }
    }
    
    public void Changed(){
		if(!isChanged){
			SortableNode node = (SortableNode)parent.getProject().SearchTree((Object)parent.getRootNode(), file);
			if(node != null){
	 		   	node.setUploaded(false);  		
			}
			parent.updateTree();
			isChanged = true;
		}
    }
    
    public void setChanged(){
	    isChanged = false;
    }
    
/*    public void refreshBrowser(){
//	   	parent.Save();
		try{
			parent.getBrowsePane().LoadPage(new URL(new String("file:///" + file)));
		}catch(MalformedURLException me){
			System.err.println(me.toString());
		}
		parent.setTitle("Scuttlebutt: " + file);
    }
*/
    
    public void keyPressed(KeyEvent e){
    }
    
    public void keyReleased(KeyEvent e){
    }
    
    
    public String toString(){
	    return file;
    }
    
    public void setEditable(boolean state){
//	    System.out.println("t isEditable:" + state);
		setEnabled(state);
	    super.setEditable(state);
    }
    
/*	public void setTabSize(int charPerTab){
		FontMetrics fm = getFontMetrics( getFont() );
		int charWidth = fm.charWidth( 'w' );
		int tabWidth = charWidth * charPerTab;
 
		TabStop[] tabs = new TabStop[10];
 
		for (int j = 0; j < tabs.length; j++)
		{
			int tab = j + 1;
			tabs[j] = new TabStop( tab * tabWidth );
		}
 
		TabSet tabSet = new TabSet(tabs);
		SimpleAttributeSet attributes = new SimpleAttributeSet();
		StyleConstants.setTabSet(attributes, tabSet);
		int length = getDocument().getLength();
		getStyledDocument().setParagraphAttributes(0, length, attributes, true);	
	}*/
    
}

